package org.steven.crm.core.service.systemuser;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import org.steven.crm.core.Constants;
import org.steven.crm.core.data.CascadeQueryConfig;
import org.steven.crm.core.data.TableRelation;
import org.steven.crm.core.data.pagination.PageSearchResult;
import org.steven.crm.core.exception.ServiceException;
import org.steven.crm.core.query.SystemUserQueryDataList;
import org.steven.crm.core.service.BaseService;
import org.steven.crm.core.service.accesstoken.AccessTokenService;
import org.steven.crm.core.service.system.SystemFileService;
import com.steven.common.utils.DateTimeUtil;
import com.steven.common.utils.MD5Util;
import com.steven.sequence.generater.SnowflakeGenerator;
import com.steven.xdb.dbmanager.DBManager;
import com.steven.xdb.pagination.query.AbstractQueryDataList;
import org.apache.commons.lang3.StringUtils;

import java.util.*;

/**
 * @Author wuzw
 * @Date 2021/5/24 21:30
 * @Version 1.0
 */
public class SystemUserService extends BaseService {

    private static SystemRoleService systemRoleService = new SystemRoleService();

    private static SystemUserRoleService systemUserRoleService = new SystemUserRoleService();

    private static AccessTokenService accessTokenService = new AccessTokenService();

    private static SystemFileService systemFileService = new SystemFileService();

    @Override
    protected String getTable() {
        return "systemUser";
    }

    @Override
    protected AbstractQueryDataList getQueryDataList() {
        return new SystemUserQueryDataList();
    }

    @Override
    protected List<CascadeQueryConfig> getCascadeQueryConfigs() {
        List<CascadeQueryConfig> cascadeQueryConfigs = new ArrayList<>();
        cascadeQueryConfigs.add(CascadeQueryConfig.builder().build().setSrcColumn("imageFileId")
                        .setValue("image").setTableRelation(TableRelation.ONE2ONE)
                        .setTargetService(systemFileService).setTargetColumn("id")
                        .setTargetTableFields(new String[] {"path"}));

        cascadeQueryConfigs.add(CascadeQueryConfig.builder().build().setSrcColumn("id")
                        .setValue("roles").setTableRelation(TableRelation.MANY2MANY)
                        .setMiddleService(systemUserRoleService).setMiddleTableSrcColumn("systemUserId")
                        .setMiddleTableTargetColumn("systemRoleId")
                        .setTargetService(systemRoleService).setTargetColumn("id")
                        .setTargetTableFields(new String[] {"name"}));

        return cascadeQueryConfigs;
    }

    @Override
    protected String getDataSourceName() {
        return null;
    }

    @Override
    public void beforeCreate(Map<String, String> data) throws Exception {
        super.beforeCreate(data);

        if (isExistsUserName("", data.get("userName"))) {
            throw new ServiceException("该账号已存在");
        }

        data.put("password", MD5Util.MD5(data.get("password")));
        String id = SnowflakeGenerator.getIndex();
        data.put("id", id);

        if (!data.get("isAdmin").equals("1")) {
            List<String> systemRoleIds = JSON.parseObject(data.get("systemRoleIds"), new TypeReference<List<String>>(){});
            systemUserRoleService.batchSave(id, systemRoleIds);
        }
    }

    @Override
    public void beforeUpdate(String id, Map<String, String> data) throws Exception {
        super.beforeUpdate(id, data);

        Map<String, String> oldData = this.read(id);
        if (oldData.isEmpty()) {
            throw new ServiceException("该账号已被删除");
        }

        if (StringUtils.isNotEmpty(data.get("userName"))) {
            if (isExistsUserName(id, data.get("userName"))) {
                throw new ServiceException("该账号已存在");
            }
        }

        if (!oldData.get("isAdmin").equals("1")
                && StringUtils.isNotEmpty(data.get("systemRoleIds"))) {
            List<String> supplierSystemRoleIds = JSON.parseObject(data.get("systemRoleIds"), new TypeReference<List<String>>(){});
            systemUserRoleService.batchSave(id, supplierSystemRoleIds);
        }
    }

    @Override
    public void afterDelete(String id) throws Exception {
        accessTokenService.delete(Constants.CLIENT.ADMIN_WEB, id);
    }

    public Map<String, Object> readEx(String id) throws Exception {
        Map<String, String> systemUser = this.read(id);
        checkUser(systemUser);

        List<String> roleIds = new ArrayList<>();
        if (!systemUser.get("isAdmin").equals("1")) {
            roleIds = systemUserRoleService.getSystemUserRoleIds(id);
        }

        Map<String, Object> systemUserInfo = new HashMap<>();
        systemUserInfo.putAll(systemUser);
        systemUserInfo.put("systemRoleIds", roleIds);

        return systemUserInfo;
    }

    public PageSearchResult<Map<String, Object>> searchEx(Map<String, String> condition, String extendSql,
                                                          List<String> extendValues, String sortSql,
                                                          int pageIndex, int pageNumber) throws Exception {
        PageSearchResult result = super.search(condition, "", new ArrayList<>(), sortSql,pageIndex, pageNumber);

        List<Map<String, String>> datas = result.getDatas();

        List<String> dataIds = new ArrayList<>();
        for (Map<String, String> data : datas) {
            dataIds.add(data.get("id"));
        }

        Map<String, List<Map<String, String>>> relateRoles = batchGetRelateRoles(dataIds);
        List<Map<String, Object>> objDatas = new ArrayList<>();
        for (Map<String, String> data : datas) {
            Map<String, Object> objData = new HashMap<>();
            objData.putAll(data);
            objData.put("roles", relateRoles.get(data.get("id")));
            objDatas.add(objData);
        }
        result.setDatas(objDatas);

        return result;
    }


    public boolean isExistsUserName(String id, String userName) throws Exception {
        Map<String, String> data = readByUserName(userName);
        return !data.isEmpty() && !data.get("id").equals(id);
    }

    /**
     * 系统用户登录
     * @param userName
     * @param password
     * @param loginIp
     * @return
     * @throws Exception
     */
    public Map<String, String> login(String clientId, String userName, String password, String loginIp) throws Exception {
        Map<String, String> data = readByUserName(userName);
        checkUser(data);

        if (!MD5Util.MD5(password).equals(data.get("password"))) {
            throw new ServiceException("登录密码不正确");
        }


        Map<String, String> v = new HashMap<>();
        v.put("loginTime", DateTimeUtil.getCurrentDateTime());
        v.put("loginIp", loginIp);
        data = this.update(data.get("id"), v);

        String token = accessTokenService.create(clientId, data.get("id")).get("token");
        data.put("token", token);

        return data;
    }

    public Map<String, Object> getInfo(String id) throws Exception {
        Map<String, String> systemUser = this.read(id);
        checkUser(systemUser);

        boolean isSupper = systemUser.get("isAdmin").equals("1");
        List<String> roleIds = systemUserRoleService.getSystemUserRoleIds(id);

        Collection<String> menuIds = isSupper ? systemRoleService.getSupperRoleMenuIds()
                : systemRoleService.getRoleMenuIds(roleIds);

        Map<String, Object> systemUserInfo = new HashMap<>();
        systemUserInfo.putAll(systemUser);
        systemUserInfo.put("menuIds", menuIds);

        return systemUserInfo;
    }

    public void logout(String clientId, String id) throws Exception {
        accessTokenService.delete(clientId, id);
    }

    public void resetPassword(String id, String oldPassword, String newPassword) throws Exception {
        Map<String, String> data = read(id);

        if (data.isEmpty()) {
            throw new ServiceException("不存在该用户");
        }

        if (StringUtils.isNotEmpty(oldPassword) &&
                !data.get("password").equals(MD5Util.MD5(oldPassword, "utf-8"))) {
            throw new ServiceException("原密码不正确");
        }

        Map<String, String> k = new HashMap<>();
        k.put("id", id);
        Map<String, String> v = new HashMap<>();
        v.put("password", MD5Util.MD5(newPassword));
        DBManager.update(getSession(), getTable(), k, v);
    }

    public Map<String, String> readByUserName(String userName) throws Exception {
        Map<String, String> k = new HashMap<>();
        k.put("userName", userName);
        k.put("deleted", "0");

        List<Map<String, String>> datas = DBManager.query(getSession(), getTable(), k);

        return datas.size() == 0 ? new HashMap<>() : datas.get(0);
    }

    private void checkUser(Map<String, String> data) throws Exception {
        if (data.isEmpty() || data.get("validFlag").equals("0")) {
            throw new ServiceException("不存在用户或用户已被冻结");
        }
    }

    private boolean checkRole(String roleId) throws Exception {
        if (StringUtils.isNotEmpty(roleId)) {
            Map<String, String> role = systemRoleService.read(roleId);
            if (role.isEmpty() || role.get("validFlag").equals("0")) {
                return false;
            }
        }

        return true;
    }

    public Map<String, List<Map<String, String>>> batchGetRelateRoles(List<String> ids) throws Exception {
        Map<String, List<String>> supplierSystemUserRelationRoleIds = systemUserRoleService.batchGetRelateRoleIds(ids);

        Set<String> roleIds = new HashSet<>();
        for (String supplierSystemUserId : supplierSystemUserRelationRoleIds.keySet()) {
            roleIds.addAll(supplierSystemUserRelationRoleIds.get(supplierSystemUserId));
        }

        Map<String, Map<String, String>> roleMap = systemRoleService.readByIdsMap(roleIds, "id, name");

        Map<String, List<Map<String, String>>> supplierSystemUserRelationRoles = new HashMap<>();
        for (String supplierSystemUserId : supplierSystemUserRelationRoleIds.keySet()) {
            List<Map<String, String>> relateRoles = new ArrayList<>();
            List<String> relateRoleIds = supplierSystemUserRelationRoleIds.get(supplierSystemUserId);
            for (String relateRoleId: relateRoleIds) {
                if (roleMap.get(relateRoleId) != null) {
                    relateRoles.add(roleMap.get(relateRoleId));
                }
            }
            supplierSystemUserRelationRoles.put(supplierSystemUserId, relateRoles);
        }

        return supplierSystemUserRelationRoles;
    }
}
